feat(fw): implement DDI HkdfDerive + KbkdfCounterHmacDerive handlers#430
Merged
Conversation
Wire up the previously-unimplemented HkdfDerive (1075) and
KbkdfCounterHmacDerive (1076) DDI ops in the firmware application
layer, deriving key material from an existing ECDH shared secret and
storing the result in the partition vault.
Behavior mirrors the reference firmware (mcr-hsm) with one deliberate
divergence: every HMAC output is stored as the variable-length HMAC
vault kind (VarLenHmacSha256/384/512) rather than the deprecated
fixed-length _HmacSha* kinds.
- Input key must be an ECDH shared secret (Secret256/384/521) with the
`derive` permission; any other kind is rejected with InvalidKeyType.
- Output key_type dispatch:
- Aes128/192/256 -> AES vault kinds (encrypt/decrypt usage)
- HmacSha256/384/512 -> VarLenHmacSha256/384/512 (sign/verify)
- VarHmac256/384/512 -> VarLenHmacSha256/384/512 (sign/verify),
key_length required (else InvalidKeyType) and range-checked
(256:32-64, 384:48-128, 512:64-128; else InvalidKeyLength)
- bulk AES / other -> InvalidKeyType (out of scope)
- HKDF runs RFC 5869 Extract-then-Expand; KBKDF runs the SP 800-108
counter-mode HMAC PRF. Absent salt/info/label/context use a
zero-length DmaBuf.
- masked_key is an empty placeholder pending the UnmaskKey handler,
consistent with the other key-creating handlers.
New: kdf.rs (shared input/output resolution), hkdf_derive.rs,
kbkdf_derive.rs, key_attrs::for_var_hmac; mod.rs dispatch wiring.
Tests: hkdf_smoke.rs / kbkdf_smoke.rs (AES round-trip + fixed-HMAC
derive on both backends; var-HMAC derive + length validation gated to
emu, since the sim has no variable-length HMAC kind).
Validation:
- emu hkdf_smoke 5/5, kbkdf_smoke 5/5; mock hkdf_smoke 2/2,
kbkdf_smoke 2/2.
- emu secret_hkdf_derive / secret_kbkdf_derive: all in-scope tests
pass (remaining failures depend on the unimplemented Hmac/OpenKey
ops and bulk AES, which were already failing as UnsupportedCmd).
- emu smoke suite 37/37; mock secret_hkdf_derive 29/29 (no regression).
- cargo xtask clippy clean; clippy --tests clean under emu and mock;
fmt and copyright clean.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR implements the previously-unhandled DDI KDF operations in the firmware MBOR application layer: HkdfDerive and KbkdfCounterHmacDerive. It validates that the input key is an ECDH shared secret with derive permission, derives key material via HKDF (RFC 5869) or KBKDF counter-mode HMAC (SP 800-108), and stores the derived key in the partition vault (with HMAC outputs stored as VarLen HMAC vault kinds).
Changes:
- Added MBOR dispatch wiring for
DdiOp::HkdfDeriveandDdiOp::KbkdfCounterHmacDerive, plus new handler modules. - Introduced shared KDF target-resolution / input-kind validation in
kdf.rs, and addedkey_attrs::for_var_hmacfor derived HMAC outputs. - Added integration smoke tests for HKDF/KBKDF derivation and wired them into the MBOR types test suite.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| fw/core/lib/src/ddi/mbor/mod.rs | Wires new DDI ops into MBOR dispatch and exports handler modules. |
| fw/core/lib/src/ddi/mbor/key_attrs.rs | Adds derived VarLen-HMAC attribute builder (for_var_hmac). |
| fw/core/lib/src/ddi/mbor/kdf.rs | Shared validation + output key-type/length → vault kind mapping logic for both KDF ops. |
| fw/core/lib/src/ddi/mbor/hkdf_derive.rs | Implements HKDF derive handler (extract + expand) and vault persistence. |
| fw/core/lib/src/ddi/mbor/kbkdf_derive.rs | Implements SP 800-108 counter-mode HMAC derive handler and vault persistence. |
| ddi/mbor/types/tests/integration/hkdf_smoke.rs | Adds HKDF smoke tests for AES and HMAC outputs. |
| ddi/mbor/types/tests/integration/kbkdf_smoke.rs | Adds KBKDF smoke tests for AES and HMAC outputs. |
| ddi/mbor/types/tests/azihsm_ddi_tests.rs | Registers the new smoke-test modules. |
vsonims
previously approved these changes
Jun 8, 2026
Previously the optional HKDF (salt/info) and SP 800-108 KBKDF
(label/context) inputs were passed as a non-optional `&DmaBuf`, where
a zero-length buffer signaled "absent". Handlers had to materialize an
empty `DmaBuf` via `split_at_mut(0)` and branch on
`match body.field.as_deref() { Some(x) => x, None => empty }`.
Make these params `Option<&DmaBuf>` through the HsmKdf trait, the std
PAL impl, the std KDF driver, and all call sites, so handlers pass
`body.field.as_deref()` directly. The std driver keeps the
empty-as-absent normalization (`owned_nonempty`) so the bytes handed to
OpenSSL stay byte-identical to the previous flow.
In hkdf_derive, `out` and `prk` are now two independent dma_alloc calls
(each 4-byte aligned) rather than carving an empty placeholder off a
shared buffer.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
vsonims
approved these changes
Jun 8, 2026
vsonims
added a commit
that referenced
this pull request
Jun 9, 2026
* feat(fw): implement DDI DeleteKey handler (#431) Wire up the previously-unimplemented DeleteKey (1014) DDI op in the firmware application layer: within an open session, delete a vault-resident key by id and return an empty success response. - Internal device keys (anything carrying the `internal` attribute — the partition unwrapping key, session and credential keys) cannot be destroyed by the host and are rejected with CannotDeleteInternalKeys, matching the reference firmware. - An unknown key_id is rejected with KeyNotFound. - Session validation is framework-level (DeleteKey -> InSession), so a missing/mismatched session returns FileHandleSessionIdDoesNotMatch. The handler is synchronous: the `internal`-attribute check and the vault deletion have no yield point between them, so no partition_lock is needed. New: delete_key.rs handler; mod.rs dispatch wiring; delete_key_smoke.rs smoke tests (delete + reuse, unknown-key, and double-delete idempotency using an app AES key, so they run on both backends). Validation: - emu delete_key_smoke 3/3; mock 3/3. - mock delete_key integration 8/8 (includes CannotDeleteInternalKeys for the unwrapping key). On emu the core delete_key integration tests pass (6/8); the unwrapping-key and bulk-import cases are setup-blocked by GetUnwrappingKey / RsaUnwrap, which are not yet implemented on main (a pre-existing limitation, not this handler) — the internal-key path is exercised on mock instead. - emu smoke 33/33 (no regression). - cargo xtask clippy clean; clippy --tests clean under emu and mock; fmt and copyright clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(fw): implement DDI HkdfDerive + KbkdfCounterHmacDerive handlers (#430) Wire up the previously-unimplemented HkdfDerive (1075) and KbkdfCounterHmacDerive (1076) DDI ops in the firmware application layer, deriving key material from an existing ECDH shared secret and storing the result in the partition vault. Behavior mirrors the reference firmware (mcr-hsm) with one deliberate divergence: every HMAC output is stored as the variable-length HMAC vault kind (VarLenHmacSha256/384/512) rather than the deprecated fixed-length _HmacSha* kinds. - Input key must be an ECDH shared secret (Secret256/384/521) with the `derive` permission; any other kind is rejected with InvalidKeyType. - Output key_type dispatch: - Aes128/192/256 -> AES vault kinds (encrypt/decrypt usage) - HmacSha256/384/512 -> VarLenHmacSha256/384/512 (sign/verify) - VarHmac256/384/512 -> VarLenHmacSha256/384/512 (sign/verify), key_length required (else InvalidKeyType) and range-checked (256:32-64, 384:48-128, 512:64-128; else InvalidKeyLength) - bulk AES / other -> InvalidKeyType (out of scope) - HKDF runs RFC 5869 Extract-then-Expand; KBKDF runs the SP 800-108 counter-mode HMAC PRF. Absent salt/info/label/context use a zero-length DmaBuf. - masked_key is an empty placeholder pending the UnmaskKey handler, consistent with the other key-creating handlers. New: kdf.rs (shared input/output resolution), hkdf_derive.rs, kbkdf_derive.rs, key_attrs::for_var_hmac; mod.rs dispatch wiring. Tests: hkdf_smoke.rs / kbkdf_smoke.rs (AES round-trip + fixed-HMAC derive on both backends; var-HMAC derive + length validation gated to emu, since the sim has no variable-length HMAC kind). Validation: - emu hkdf_smoke 5/5, kbkdf_smoke 5/5; mock hkdf_smoke 2/2, kbkdf_smoke 2/2. - emu secret_hkdf_derive / secret_kbkdf_derive: all in-scope tests pass (remaining failures depend on the unimplemented Hmac/OpenKey ops and bulk AES, which were already failing as UnsupportedCmd). - emu smoke suite 37/37; mock secret_hkdf_derive 29/29 (no regression). - cargo xtask clippy clean; clippy --tests clean under emu and mock; fmt and copyright clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(fw): implement DDI Hmac handler (#445) Wire up the previously-unimplemented Hmac (1077) DDI op in the firmware application layer: compute an HMAC tag over a host-supplied message using a vault-resident HMAC key. - The key may be any HMAC kind — fixed-length _HmacSha* or variable-length VarLenHmacSha* (the latter is what HKDF / KBKDF derive in this firmware). The key's hash variant selects the MAC algorithm and tag length (SHA-256/384/512 -> 32/48/64 bytes), matching the reference firmware's Hmac handler. - An unknown key_id surfaces as KeyNotFound; a non-HMAC key as InvalidKeyType. - Generating a MAC is a PKCS#11 C_Sign operation, so the key must carry CKA_SIGN; a derive-only HMAC key is rejected with InvalidPermissions. Deriving an HMAC key via KBKDF with no label/context (as the hmac integration tests do) previously failed: azihsm_crypto's KbkdfAlgo rejected deriving with both Label and Context absent. SP 800-108 permits empty Label/Context (and the sim/reference allow it), so relax KbkdfAlgo to permit both-absent — the PRF input reduces to the counter (and optional length field), leaving all other cases byte-identical. New: hmac.rs handler, from_pal::hmac_hash; mod.rs dispatch wiring; hmac_smoke.rs smoke tests (fixed-HMAC MAC + unknown-key on both backends; var-HMAC MAC and the derive-only sign-permission rejection gated to emu, since the sim has no var-len HMAC kind and only permits SignVerify on HMAC keys). Validation: - emu integration::hmac:: 17/17 and hmac_smoke 4/4; mock 10/10 and hmac_smoke 2/2. - The Hmac op + KBKDF change advance secret_hkdf_derive and secret_kbkdf_derive from 15/29 to 19/29 each on emu (the *_aes*_sha* HMAC-roundtrip tests now pass; remaining failures are bulk AES and the unimplemented OpenKey op). - azihsm_crypto 476/476; std PAL unit 128/128; emu smoke 50/50; hkdf_smoke / kbkdf_smoke pass on emu and mock (no regression). - cargo xtask clippy clean; clippy --tests clean under emu and mock; fmt and copyright clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Jayant Gandhi <jayg@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wire up the previously-unimplemented HkdfDerive (1075) and KbkdfCounterHmacDerive (1076) DDI ops in the firmware application layer, deriving key material from an existing ECDH shared secret and storing the result in the partition vault.
Behavior mirrors the reference firmware (mcr-hsm) with one deliberate divergence: every HMAC output is stored as the variable-length HMAC vault kind (VarLenHmacSha256/384/512) rather than the deprecated fixed-length _HmacSha* kinds.
derivepermission; any other kind is rejected with InvalidKeyType.key_length required (else InvalidKeyType) and range-checked
(256:32-64, 384:48-128, 512:64-128; else InvalidKeyLength)
counter-mode HMAC PRF. Absent salt/info/label/context use a
zero-length DmaBuf.
consistent with the other key-creating handlers.
New: kdf.rs (shared input/output resolution), hkdf_derive.rs, kbkdf_derive.rs, key_attrs::for_var_hmac; mod.rs dispatch wiring. Tests: hkdf_smoke.rs / kbkdf_smoke.rs (AES round-trip + fixed-HMAC derive on both backends; var-HMAC derive + length validation gated to emu, since the sim has no variable-length HMAC kind).
Validation: